﻿// Copyright 2009 Mike Geise
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Falcon.Core.Services
{
	using System;
	using System.Globalization;
	using System.IO;
	using Falcon.Core.Collections;
	using Falcon.Core.Domain;
	using Falcon.Core.Domain.Repositories;
	using Falcon.Core.Domain.Services;
	using Falcon.Core.Domain.Values;
	using Falcon.Core.Extensions;
	using Falcon.Core.Validation;

	public class AttachmentService : IAttachmentService
	{
		// private constants
		private const string ThumbFormat = "{0}_{1}_{2}{3}";

		/// <summary>
		/// Initializes a new instance of the <see cref="AttachmentService"/> class.
		/// </summary>
		/// <param name="fileService">The file service.</param>
		/// <param name="imageService">The image service.</param>
		/// <param name="attachmentRuleService">The attachment rule service.</param>
		/// <param name="attachmentRepository">The attachment repository.</param>
		public AttachmentService(
			IFileService fileService,
			IImageService imageService, 
			IAttachmentRuleService attachmentRuleService,
			IAttachmentRepository attachmentRepository)
		{
			this.FileService = fileService;
			this.ImageService = imageService;
			this.AttachmentRuleService = attachmentRuleService;
			this.AttachmentRepository = attachmentRepository;
		}

		/// <summary>
		/// Gets or sets the file service.
		/// </summary>
		/// <value>The file service.</value>
		public IFileService FileService
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets or sets the image service.
		/// </summary>
		/// <value>The image service.</value>
		public IImageService ImageService
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets or sets the attachment rule service.
		/// </summary>
		/// <value>The attachment rule service.</value>
		public IAttachmentRuleService AttachmentRuleService
		{
			get; 
			private set;
		}

		/// <summary>
		/// Gets or sets the attachment repository.
		/// </summary>
		/// <value>The attachment repository.</value>
		public IAttachmentRepository AttachmentRepository
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets the attachment by id.
		/// </summary>
		/// <param name="id">The id.</param>
		/// <returns></returns>
		public Attachment GetById(int id)
		{
			return this.AttachmentRepository.GetById(id);
		}

		/// <summary>
		/// Gets all the attachment.
		/// </summary>
		/// <param name="specification">The <see cref="AttachmentSpecification"/>.</param>
		/// <returns></returns>
		public IPagedList<Attachment, AttachmentSpecification> GetPaged(AttachmentSpecification specification)
		{
			return this.AttachmentRepository.GetPaged(specification);
		}

		/// <summary>
		/// Gets the thumbnail image for the attachment.
		/// </summary>
		/// <param name="attachment">The attachment.</param>
		/// <param name="width">The width.</param>
		/// <param name="height">The height.</param>
		/// <returns></returns>
		public FileInfo GetThumbnail(Attachment attachment, int width, int height)
		{
			if (attachment.Width <= width && attachment.Height <= height)
			{
				return new FileInfo(attachment.FullPath.AsPath());
			}

			var name = ThumbFormat.Fmt(
				Path.GetFileNameWithoutExtension(attachment.FileDiskName),
				width,
				height,
				Path.GetExtension(attachment.FileDiskName)
			);

			var path = Path.Combine(attachment.FilePath, name);
			var file = new FileInfo(path.AsPath());

			if (!file.Exists)
			{
				this.ImageService.Resize(name, attachment.FullPath, path, width, height, Settings.Attachments.ThumbAspect);
			}

			return file;
		}

		/// <summary>
		/// Creates the attachment.
		/// </summary>
		/// <returns></returns>
		public Attachment Create()
		{
			var attachment = new Attachment
			{
				Created = DateTime.Now
			};

			attachment.Updated = attachment.Created;

			return attachment;
		}

		/// <summary>
		/// Inserts the specified attachment.
		/// </summary>
		/// <param name="attachment">The attachment.</param>
		public void Insert(Attachment attachment)
		{
			this.AttachmentRepository.Insert(attachment);
		}

		/// <summary>
		/// Inserts the specified attachment.
		/// </summary>
		/// <param name="attachment">The attachment.</param>
		/// <param name="value">The value.</param>
		public void Insert(Attachment attachment, AttachmentCreate value)
		{
			var identity = User.Identity;

			if (!identity.IsAuthenticated())
			{
				return;
			}

			ValidateRules(value);

			var path = GetPath();
			var file = this.FileService.Save(value.Source, path);
			var dimensions = value.Source.GetImageDimensions();

			attachment.UserId = identity.Id;
			attachment.FileName = file.FileName;
			attachment.FileDiskName = file.FileDiskName;
			attachment.FileType = file.FileType;
			attachment.FileExtension = file.FileExtension;
			attachment.FilePath = file.FilePath.Replace("\\", "/");
			attachment.FileSize = file.FileSize;
			attachment.Width = dimensions[0];
			attachment.Height = dimensions[1];

			this.Insert(attachment);
		}

		/// <summary>
		/// Deletes the specified attachment.
		/// </summary>
		/// <param name="attachment">The attachment.</param>
		public void Delete(Attachment attachment)
		{
			this.FileService.Delete(attachment.FullPath.AsPath());
			this.AttachmentRepository.Delete(attachment);
		}

		/// <summary>
		/// Gets the attachment path.
		/// </summary>
		/// <returns></returns>
		private static string GetPath()
		{
			var date = DateTime.Now;

			// month/day/year
			var year = date.Year.ToString(CultureInfo.InvariantCulture);
			var month = Path.Combine(year, date.Month.ToString(CultureInfo.InvariantCulture));
			var day = Path.Combine(month, date.Day.ToString(CultureInfo.InvariantCulture));

			// path_to_attachments/month/day/year
			var path = Path.Combine(Settings.Attachments.Path, day);
			var directory = new DirectoryInfo(path.AsPath());

			if (!directory.Exists)
			{
				directory.Create();
			}

			return path;
		}

		/// <summary>
		/// Validates the attachment rules.
		/// </summary>
		/// <param name="value">The value.</param>
		private void ValidateRules(AttachmentCreate value)
		{
			var validation = value.Rules();

			var rules = this.AttachmentRuleService.GetPaged(new AttachmentRuleSpecification
			{
				Group = value.Group
			});

			var valid = false;

			foreach (var rule in rules)
			{
				if (rule.ContentType.Equals(value.Source.ContentType, StringComparison.OrdinalIgnoreCase))
				{
					valid = true;
				}

				if (rule.FileSize > 0 && rule.FileSize < value.Source.ContentLength)
				{
					validation.Result.Add(new ValidationError
					{
						Bind = value.Bind,
						Message = Resources.Errors.FileLengthNotValid.Fmt(rule.FileSize.ParseFileSize())
					});
				}

				if (rule.Width <= 0 || rule.Height <= 0)
				{
					continue;
				}

				var dimensions = value.Source.GetImageDimensions();

				if (dimensions[0] > rule.Width && dimensions[1] > rule.Height)
				{
					validation.Result.Add(new ValidationError
					{
						Bind = value.Bind,
						Message = Resources.Errors.ImageDimMaxNotValid.Fmt(rule.Width, rule.Height)
					});
				}
			}

			if (!valid)
			{
				validation.Result.Add(new ValidationError
				{
					Bind = value.Bind,
					Message = Resources.Errors.FileContentTypeNotValid.Fmt(value.Source.ContentType)
				});
			}

			validation.Validate(value);
		}
	}
}